package com.exit66.jukebox;

/**
 * @author andyb
 *
 * To change this generated comment edit the template variable "typecomment":
 * Window>Preferences>Java>Templates.
 * To enable and disable the creation of type comments go to
 * Window>Preferences>Java>Code Generation.
 */

import java.io.File;
import java.util.Map;
import javazoom.jlgui.basicplayer.BasicController;
import javazoom.jlgui.basicplayer.BasicPlayer;
import javazoom.jlgui.basicplayer.BasicPlayerListener;
import javazoom.jlgui.basicplayer.BasicPlayerEvent;
import javazoom.jlgui.basicplayer.BasicPlayerException;
import com.exit66.jukebox.data.Library;
import com.exit66.jukebox.data.Playlist;
import com.exit66.jukebox.data.RequestQueue;
import com.exit66.jukebox.data.Track;

public class SoundPlayer extends Thread implements BasicPlayerListener {
    
    private static final int STATUS_STOPPED = 0;
    private static final int STATUS_PLAYING = 1;
    private static final int STATUS_SHUTDOWN = 2;
    
    int currentStatus = STATUS_STOPPED;
    BasicPlayer mainPlayer;
	BasicPlayer nextPlayer;;
	private int queuedTrackID = -1;
	private Map currentTrackInfo;
	private Map queuedTrackInfo;
	private long currentTrackLengthMilliseconds = 0;
	
    private static SoundPlayer ref;
    public int currentTrackID = -1;
    
    public static SoundPlayer getSoundPlayer() {
        if (ref == null)
            ref = new SoundPlayer();
        return ref;
    }
    
    public Object clone()
    throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
    
    private SoundPlayer() {
    	currentStatus = STATUS_STOPPED;
        setName("Exit 66 Player");
        start();
    }
    
    public void run() {
        try {
            waitForNext();
        } catch(Exception exception) {
            System.out.println(exception);
        }
    }
    
    private void waitForNext() {
        try {
            while(true) {
                if(currentStatus != STATUS_STOPPED)
                    break;
                if(!nextSong())
                    Thread.sleep(5000L);
            }
            if(currentStatus == STATUS_SHUTDOWN)
                return;
        } catch(Exception exception) {
            System.err.println(exception);
        }
    }
    
    private boolean nextSong() {
        RequestQueue requestqueue = new RequestQueue();
        int i = requestqueue.getNextRequest();
        if(i == -1) {
            i = Playlist.getNextTrackID();
        }
        if(i != -1) {
            Track track = new Track();
            track.setTrackID(i);
            track.playTrack();
            track.fetch();
            if(track.getLibraryID() != -1) {
                Library library = new Library();
                library.setLibraryID(track.getLibraryID());
                library.fetch();
                if(track.getFileName().length() != 0 && library.getLibraryPath().length() != 0) {
                    queueSong(i, library.getLibraryPath().concat(track.getFileName()));
                    try {
                        Thread.sleep(1000L);
                    } catch(Exception exception) { }
                    return true;
                } else {
                	
                    return false;
                }
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    
    private void playSong() {
    	try {
	    	mainPlayer = nextPlayer;
			nextPlayer = null;
			SoundPlayer.getSoundPlayer().currentTrackID = queuedTrackID;
			queuedTrackID = -1;
			currentTrackInfo = queuedTrackInfo;
	    	currentTrackLengthMilliseconds = getTimeLengthEstimation(currentTrackInfo);
			mainPlayer.play();
			currentStatus = STATUS_PLAYING;
    	} catch(Exception exception) {
    		mainPlayer = null;
            System.err.print(exception);
            currentStatus = STATUS_STOPPED;
            SoundPlayer.getSoundPlayer().currentTrackID = -1;
        }
    }
    
    private void queueSong(int i, String s) {
        try {
        	nextPlayer = new BasicPlayer();
        	nextPlayer.addBasicPlayerListener(this);
        	nextPlayer.open(new File(s));
        	nextPlayer.pause();
        	queuedTrackID = i;
        	if (mainPlayer == null) {
        		playSong();
        	}       		
            
        } catch(Exception exception) {
            System.err.print(exception);
            currentStatus = STATUS_STOPPED;
            SoundPlayer.getSoundPlayer().currentTrackID = -1;
        }
    }
    
    public void playPause() {
    	if (currentStatus == STATUS_PLAYING) {
    		try {
		    	if (mainPlayer.getStatus() == BasicPlayer.PLAYING) {
		    		mainPlayer.pause();
		    	}
				else {
					mainPlayer.resume();
				}
    		}
    		catch (BasicPlayerException e) {
    			System.err.println(e);
    		}
    	}
    }
    
    public void setController(BasicController basicController) {
        
    }
    
    public void opened(Object stream, Map properties) {
    	queuedTrackInfo = properties;
    }
    
    public void stateUpdated(BasicPlayerEvent event) {
        /*-- End Of Media reached --*/
        int state = event.getCode();
        if (state==BasicPlayerEvent.EOM) {
            songComplete();
        }
    }
    
    private void songComplete() {
    	System.out.println("done playing");
        mainPlayer = null; 
    	SoundPlayer.getSoundPlayer().currentTrackID = -1;
        if (currentStatus != STATUS_SHUTDOWN) {
            currentStatus = STATUS_STOPPED;
        }
        //if (nextPlayer == null)
        	waitForNext();
        //else
        //	playSong();
    }
    
    public void progress(int bytesread, long microseconds, byte[] pcmdata, Map properties) {
    	if (currentTrackLengthMilliseconds == 0)
    		return;
    	//if ((nextPlayer == null) && (remainingTime <= 10000)) {
    	//	nextSong();
    	//}
    }
    
    public void stopSong() {
        currentStatus = STATUS_STOPPED;
        try {
        	mainPlayer.stop();
        	mainPlayer = null;
            songComplete();
        } catch (BasicPlayerException e) {
            System.err.println(e);
        }
    }
    
    public void shutDown() {
        currentStatus = STATUS_SHUTDOWN;
        try {
        	if (mainPlayer != null) 
        		mainPlayer.stop();
        } catch (BasicPlayerException e) {
            System.err.println(e);
        }
    }
    
    private long getTimeLengthEstimation(Map properties) {
		long milliseconds = -1;
		int byteslength = -1;
		if (properties != null) {
			if (properties.containsKey("audio.length.bytes")) {
				byteslength = ((Integer) properties.get("audio.length.bytes"))
						.intValue();
			}
			if (properties.containsKey("duration")) {
				milliseconds = (int) (((Long) properties.get("duration"))
						.longValue()) / 1000;
			} else {
				// Try to compute duration
				int bitspersample = -1;
				int channels = -1;
				float samplerate = -1.0f;
				int framesize = -1;
				if (properties.containsKey("audio.samplesize.bits")) {
					bitspersample = ((Integer) properties
							.get("audio.samplesize.bits")).intValue();
				}
				if (properties.containsKey("audio.channels")) {
					channels = ((Integer) properties.get("audio.channels"))
							.intValue();
				}
				if (properties.containsKey("audio.samplerate.hz")) {
					samplerate = ((Float) properties.get("audio.samplerate.hz"))
							.floatValue();
				}
				if (properties.containsKey("audio.framesize.bytes")) {
					framesize = ((Integer) properties
							.get("audio.framesize.bytes")).intValue();
				}
				if (bitspersample > 0) {
					milliseconds = (int) (1000.0f * byteslength / (samplerate
							* channels * (bitspersample / 8)));
				} else {
					milliseconds = (int) (1000.0f * byteslength / (samplerate * framesize));
				}
			}
		}
		return milliseconds;
	}
    
}
